package me.zhengjie.modules.system.service.impl

import jakarta.persistence.criteria.CriteriaBuilder
import jakarta.persistence.criteria.CriteriaQuery
import jakarta.persistence.criteria.Root
import jakarta.servlet.http.HttpServletResponse
import me.zhengjie.common.config.FileProperties
import me.zhengjie.common.exception.BadRequestException
import me.zhengjie.common.exception.EntityExistException
import me.zhengjie.common.exception.EntityNotFoundException
import me.zhengjie.common.utils.*
import me.zhengjie.common.utils.FileUtil.downloadExcel
import me.zhengjie.modules.security.service.OnlineUserService
import me.zhengjie.modules.security.service.UserCacheManager
import me.zhengjie.modules.system.domain.User
import me.zhengjie.modules.system.repository.UserRepository
import me.zhengjie.modules.system.service.UserService
import me.zhengjie.modules.system.service.dto.*
import me.zhengjie.modules.system.service.mapper.UserLoginMapper
import me.zhengjie.modules.system.service.mapper.UserMapper
import org.springframework.beans.factory.annotation.Value
import org.springframework.cache.annotation.CacheConfig
import org.springframework.cache.annotation.CacheEvict
import org.springframework.cache.annotation.Cacheable
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional
import org.springframework.web.multipart.MultipartFile
import java.io.File
import java.io.IOException
import java.util.*
import java.util.stream.Collectors

/**
 * @author Kuki Wu
 * @date 2024-11-23
 */
@Service
@CacheConfig(cacheNames = ["sys_user"])
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = [Exception::class])
class UserServiceImpl(
    private val userRepository: UserRepository,
    private val userMapper: UserMapper,
    private val userLoginMapper: UserLoginMapper,
    private val properties: FileProperties,
    private val redisUtils: RedisUtil,
    private val userCacheManager: UserCacheManager,
    private val onlineUserService: OnlineUserService
) : UserService {

    override fun queryAll(criteria: UserQueryCriteria, pageable: Pageable?): PageResult<UserDTO?>? {
        val page = userRepository.findAll(
            { root: Root<User?>, criteriaQuery: CriteriaQuery<*>?, criteriaBuilder: CriteriaBuilder ->
                QueryHelp.getPredicate(
                    root,
                    criteria,
                    criteriaBuilder
                )
            }, pageable!!
        )
        return PageUtil.toPage(page.map { userMapper.toDto(it!!) }.content, page.totalElements)
    }

    @Cacheable(key = "'queryall_criteria'")
    override fun queryAll(criteria: UserQueryCriteria): List<UserDTO> {
        val users =
            userRepository.findAll { root: Root<User?>, criteriaQuery: CriteriaQuery<*>?, criteriaBuilder: CriteriaBuilder ->
                QueryHelp.getPredicate(
                    root,
                    criteria,
                    criteriaBuilder
                )
            }
        return userMapper.toDto(users as List<User>) as List<UserDTO>
    }

    @Cacheable(key = "'findbyid'+#p0")
    @Transactional
    override fun findById(id: Long): UserDTO? {
        val user = userRepository.findById(id).orElseGet { User() }
        ValidationUtil.isNull(user!!.id, "User", "id", id)
        return userMapper.toDto(user)
    }

    @CacheEvict(allEntries = true)
    @Transactional(rollbackFor = [Exception::class])
    override fun create(resources: User): UserDTO? {
        if (userRepository.findByUsername(resources.username) != null) {
            throw EntityExistException(User::class.java, "username", resources.username!!)
        }
        if (userRepository.findByEmail(resources.email) != null) {
            throw EntityExistException(User::class.java, "email", resources.email!!)
        }

        // 默认密码 123456，此密码是加密后的字符
        resources.password = "e10adc3949ba59abbe56e057f20f883e"
        return userMapper.toDto(userRepository.save(resources))
    }

    @CacheEvict(allEntries = true)
    @Transactional(rollbackFor = [Exception::class])
    override fun update(resources: User) {
        val user = userRepository.findById(resources.id!!).orElseGet { User() }
        ValidationUtil.isNull(user!!.id, "User", "id", resources.id!!)
        val user1 = userRepository.findByUsername(user.username)
        val user2 = userRepository.findByEmail(user.email)
        val user3 = userRepository.findByPhone(user.phone)
        if (user1 != null && user.id != user1.id) {
            throw EntityExistException(User::class.java, "username", resources.username!!)
        }
        if (user2 != null && user.id != user2.id) {
            throw EntityExistException(User::class.java, "email", resources.email!!)
        }
        if (user3 != null && user.id != user3.id) {
            throw EntityExistException(User::class.java, "phone", resources.phone!!)
        }

        // 如果用户的角色改变了，需要手动清理下缓存
        if (resources.roles != user.roles) {
            redisUtils.del(CacheKey.DATA_USER + resources.id)
            redisUtils.del(CacheKey.MENU_USER + resources.id)
            redisUtils.del(CacheKey.ROLE_AUTH + resources.id)
        }
        // 如果用户被禁用，则清除用户登录信息
        // 如果用户被禁用，则清除用户登录信息
        if (!resources.enabled!!) {
            onlineUserService.kickOutForUsername(resources.username!!)
        }

        user.username = resources.username
        user.email = resources.email
        user.enabled = resources.enabled
        user.roles = resources.roles
        user.dept = resources.dept
        user.job = resources.job
        user.phone = resources.phone
        user.nickName = resources.nickName
        user.gender = resources.gender

        userRepository.save(user)

        // 清除缓存
        delCaches(user.id!!, user.username!!)
    }

    @CacheEvict(allEntries = true)
    @Transactional(rollbackFor = [Exception::class])
    override fun delete(id: Long) {
        userRepository.deleteById(id)
    }

    @Transactional(rollbackFor = [java.lang.Exception::class])
    override fun updateCenter(resources: User?) {
        val user = userRepository.findById(resources!!.id!!).orElseGet { User() }!!
        val user1 = userRepository.findByPhone(resources.phone)
        if (user1 != null && user.id != user1.id) {
            throw EntityExistException(User::class.java, "phone", resources.phone!!)
        }
        user.nickName = resources.nickName
        user.phone = resources.phone
        user.gender = resources.gender
        userRepository.save(user)
        // 清理缓存
        delCaches(user.id!!, user.username!!)
    }
    //@Cacheable(key = "'loadUserByUsername:'+#p0")
    override fun findByName(userName: String?): UserDTO? {
        var user: User? = if (ValidationUtil.isEmail(userName)) {
            userRepository.findByEmail(userName)
        } else {
            userRepository.findByUsername(userName)
        }

        return if (user == null) {
            throw EntityNotFoundException(User::class.java, "name", userName!!)
        } else {
            userMapper.toDto(user)
        }
    }

    override fun getLoginData(userName: String?): UserLoginDTO?{
        val user = userRepository.findByUsername(userName)
        return if (user == null) {
            throw EntityNotFoundException(User::class.java, "name", userName!!)
        } else {
            userLoginMapper.toDto(user)
        }
    }
    @CacheEvict(allEntries = true)
    @Transactional(rollbackFor = [Exception::class])
    override fun updatePass(username: String?, pass: String?) {
        userRepository.updatePass(username, pass, Date())
    }

    @CacheEvict(allEntries = true)
    @Transactional(rollbackFor = [Exception::class])
    override fun updateAvatar(multipartFile: MultipartFile):Map<String, String> {
        // 文件大小验证
        FileUtil.checkSize(properties.avatarMaxSize!!, multipartFile.size)
        // 验证文件上传的格式
        // 验证文件上传的格式
        val image = "gif jpg png jpeg"
        val fileType: String = FileUtil.getExtensionName(multipartFile.originalFilename)!!
        if (fileType != null && !image.contains(fileType)) {
            throw BadRequestException("文件格式错误！, 仅支持 $image 格式")
        }
        val user = userRepository.findByUsername(SecurityUtils.username)
        val oldPath = user!!.avatarPath
        val file: File = FileUtil.upload(multipartFile, properties.getPath()!!.avatar!!)!!
        user.avatarPath = Objects.requireNonNull(file).path
        user.avatarName = file.name
        userRepository.save(user)
        if (org.apache.commons.lang3.StringUtils.isNotBlank(oldPath)) {
            FileUtil.delFile(oldPath!!)
        }

        val username = user.username

        flushCache(username!!)
        return  hashMapOf<String, String>().apply {
                put("avatar", file.name)
        }
    }

    @CacheEvict(allEntries = true)
    @Transactional(rollbackFor = [Exception::class])
    override fun updateEmail(username: String?, email: String?) {
        userRepository.updateEmail(username, email)
    }

    @Throws(IOException::class)
    override fun download(queryAll: List<UserDTO>, response: HttpServletResponse?) {
        val list: MutableList<Map<String?, Any?>?> = ArrayList()
        for (userDTO in queryAll) {
            val roles: List<*> = userDTO.roles!!.stream().map { obj: RoleSmallDTO -> obj.name!! }
                .collect(Collectors.toList())
            val map: MutableMap<String?, Any?> = LinkedHashMap()
            map["用户名"] = userDTO.username
            map["头像"] = userDTO.avatarName
            map["头像路径"] = userDTO.avatarPath
            map["邮箱"] = userDTO.email
            map["状态"] = if (userDTO.enabled == true) "启用" else "禁用"
            map["手机号码"] = userDTO.phone
            map["角色"] = roles
            map["部门"] = userDTO.dept!!.name
            map["岗位"] = userDTO.job!!.map { obj: JobSmallDTO -> obj.name }.toList()
            map["最后修改密码的时间"] = userDTO.pwdResetTime
            map["创建日期"] = userDTO.createTime
            list.add(map)
        }
        downloadExcel(list, response!!)
    }

    @Transactional(rollbackFor = [java.lang.Exception::class])
    override fun resetPwd(ids: Set<Long?>?, pwd: String?) {
        userRepository.resetPwd(ids, pwd)
    }

    /**
     * 清理缓存
     *
     * @param id /
     */
    fun delCaches(id: Long, username: String) {
        redisUtils.del(CacheKey.USER_ID + id)
        flushCache(username)
    }

    /**
     * 清理 登陆时 用户缓存信息
     *
     * @param username /
     */
    private fun flushCache(username: String) {
        userCacheManager.cleanUserCache(username)
    }
}